/**
 * OPMERKINGEN OVER KLASSE
//TODO
 * De shipInteraction methodes herwerken naar this & de dubbele argumenten houden voor in facade en daar laten uitvoeren op bv ship1. Idem voor getTotalVelocity, zonder argumenten laten werken
 * MaxBounces moet int zijn, maar dan bestaat geen infinity voor ship en asteroid
 * Massa instellen
 *
 */

package asteroids.model;

import ModelExceptions.*;
import asteroids.Util;
import be.kuleuven.cs.som.annotate.*;
/**
 * 
 * @author 	Thomas & Carsten
 * @Date 	16/04/2013
 *
 */
public abstract class CollisionObject {


	/**
	 * This new collisionobject will be created with the given specifications in the arguments.
	 * @param xCoordinate				The current x-value of the collisionobject center (2d)
	 * @param yCoordinate				The current y-value of the collisionobject center (2d)
	 * @param xVelocity					The current rate at which the x-value changes
	 * @param yVelocity					The current rate at which the y-value changes
	 * @param radius					The distance between the center of the collisionobject and the outer rim
	 * @param angle						The direction that the collisionobject is pointing at (2d)
	 * @param mass 						The weight of the collisionobject
	 * @throws IllegalRadiusException	If the given radius is smaller than the radiusconstraint, than the exception is thrown
	 * @throws OutOfBoundsException		If one (or both) of the coordinates aren't numbers, than the exception is thrown
	 * @post	If radius is valid, this collisionobject will have the value radius for its radius
	 * 			|if(isValidRadius(radius)
	 * 			|	new.getRadius() == radius
	 * @post	If the given xCoordinate is valid, this collisionobject will have as new x-coordinate the value of xCoordinate
	 * 			|if(isValidCoordinate(yCoordinate))
	 * 				new.getXCoordinate() == xCoordinate
	 * @post	If the given yCoordinate is valid, this ship will have as new y-coordinate the value of yCoordinate
	 * 			|if(isValidCoordinate(yCoordinate))
	 * 				new.getYCoordinate() == yCoordinate
	 * @post	If the given combination of xVelocity and yVelocity is valid, than this collisionobject will have as x-velocity the value of xVelocity and as y-velocity the value of yVelocity
	 * 			| if(isValidVelocity(xVelocity, yVelocity){
	 * 			|	new.getXVelocity() == xVelocity && new.getYVelocity() == yVelocity;
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 * @invar	The mass of a ship has to be larger than 0
	 * 			|isValidMass(getMass()) == true	
	 * @invar ...
	 * 			| if(isInWorld)
	 * 			| getXCoordinate() <= getWorld().getRightBoundary() && getXCoordinate >= getLeftBoundary() && getYCoordinate() <= getWorld().getUpperBoundary() && getYCoordinate() >= getWorl().getLowerBoundary	
	 */ 
	//MASS VERDER UITWERKEN MET DENSITY VS MASS ALS PARAMETER
	protected CollisionObject (double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius)//,double angle,double mass) 
			throws OutOfBoundsException,IllegalRadiusException{

		if(!isValidRadius(radius)){
			throw new IllegalRadiusException("That is an illegal radius");
		}
		setCoordinate(xCoordinate, yCoordinate);
		setValidVelocity(xVelocity,yVelocity);
		setMaximumMass();
		setMinimumMass();
		setMass(mass); 						//ZO KOMT HIJ EVEN IN EEN ILLEGALE TOESTAND...
		this.radius=radius;	
		this.state = State.NOWORLD;
	}

	private int getXPositionInVector(){
		return this.xPositionInVector;
	}
	private int getYPositionInVector(){
		return this.yPositionInVector;
	}
	private final int xPositionInVector = 0;
	private final int yPositionInVector = 1;



	/**
	 * Get the x-coordinate from this CollisionObject
	 * @return The x-coordinate of this collisionobject.
	 */
	@Basic @Raw
	public double getXCoordinate() {
		return getPositionVector().getValueAtPosition(getXPositionInVector());	
	}	

	/**
	 * 
	 * @param xCoordinate				The current x-value of the collisionobject center (2d)
	 * @throws OutOfBoundsException		If the xCoordinate isn't a number, than the exception is thrown
	 * 									|!isValidCoordinate
	 * @post (new this).getXCoordinate() == xCoordinate
	 */
	private void setXCoordinate(double xCoordinate) throws OutOfBoundsException{
		if(isValidCoordinate(xCoordinate)){
			positionVector = new Vector(xCoordinate, getYCoordinate());
		}
	}

	@Basic @Raw
	public double getYCoordinate() {
		return positionVector.getValueAtPosition(getYPositionInVector());
	}

	/**
	 * @param yCoordinate				The current y-value of the ships center (2d)
	 * @throws OutOfBoundsException		If the yCoordinate isn't a number, than the exception is thrown
	 * @post (new this).getYCoordinate() == yCoordinate
	 */
	private void setYCoordinate(double yCoordinate) {
		if (isValidCoordinate(yCoordinate) == true){
			positionVector = new Vector(getXCoordinate(),yCoordinate); 
		}
	}


	/**
	 * Assign a new coordinate to the collisionobject.
	 * @param xCoordinate 	The x-coordinate you want to assign
	 * @param yCoordinate 	The y-coordinate you want to assign
	 * @post	If the given coordinates are valid, those coordinates will be assigned.	
	 * 			|if(isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			|	new.getXCoordinate() == xCoordinate
	 * 			|	&& new.getYCoordinate() == yCoordinate		
	 * @throws IllegalPositionException	Is thrown when the given coordinates are invalid
	 * 									|if(!isValidCoordinate(xCoordinate)  || !isValidCoordinate(yCoordinate))
	 * 									|	throw new IllegalPositionException
	 */
	@Basic
	@Raw
	public void setCoordinate(double xCoordinate, double yCoordinate) throws IllegalPositionException{
		if (isValidCoordinate(xCoordinate)  && isValidCoordinate(yCoordinate)){
			positionVector = new Vector(xCoordinate,yCoordinate);
		}
		else
			throw new IllegalPositionException("The given coordinates are invalid to assign");
		//TODO catchen in facade
	}

	/**
	 * 
	 * @param duration	The time that the movement took
	 * @post	if the duration is a valid value, then the ship will move a certain amount,
	 * 			based on the current position and the velocity 
	 * 			|if(isValidDuratation(duration){
	 * 			|(new this).getXCoordinate() == xCoordinate + xVelocity*duration
	 * 			|&& (new this).getYCoordinate() == yCoordinate + yVelocity*duration}
	 * @post 	if the given duration is a non acceptable time then an IllegalTimeDurationException is thrown, no change of state of the object
	 * 			|if(!isValidDuration(duration){
	 * 			|	new.getXCoordinate() == getXCoordinate()
	 * 			|   new.getYcoordinate() == getYCoordinate()
	 * @throws IllegalTimeDurationException	Is thrown when the given time is invalid.
	 * 										|(!isValidDuration(duration)) throw new IllegalTimeDuration
	 * @throws IllegalPositionException		Is thrown when the position after the move is invalid.
	 * 										|!isValidCoordinate(this.move(duration).getXCoordinate()) || !isValidCoordinate(this.move(duration).getYCoordinate()
	 * 
	 */
	public void move2(double duration) throws IllegalTimeDurationException, IllegalPositionException {
		//if(isInWorld()){
			if(isValidDuration(duration)){
				setCoordinate(getXCoordinate() + getXVelocity()*duration, getYCoordinate() + getYVelocity()*duration);
				if(exceedsWorldBoundaries())
					bounceOffBoundary();
			}
			else{
				throw new IllegalTimeDurationException("That's an illegal time");
			}
		}
	//}
	public void move(double duration) throws IllegalTimeDurationException, IllegalPositionException {
		double timeToFirstBorderCollission = Double.POSITIVE_INFINITY;
		if(isInWorld() && !Util.fuzzyEquals(0, duration)){
			if ( isValidDuration(duration)){ //&& isInWorld()){
				timeToFirstBorderCollission = calculateTimeToFirstCollision();

				if(Util.fuzzyLessThanOrEqualTo(duration,timeToFirstBorderCollission)){
					setCoordinate(getXCoordinate() + getXVelocity()*duration, getYCoordinate() + getYVelocity()*duration);}
				else{
					setCoordinate(getXCoordinate() + getXVelocity()*timeToFirstBorderCollission, getYCoordinate() + getYVelocity()*timeToFirstBorderCollission);
					bounceOffBoundary();
					double yu = duration*100000-timeToFirstBorderCollission*100000;
					if(	Util.fuzzyLessThanOrEqualTo(yu, 0)){
						
					}
					else
						move(duration-timeToFirstBorderCollission)	;					
				}
			}
			else{
				throw new IllegalTimeDurationException("That's an illegal time");
			}}
	}
	private double calculateTimeToFirstCollision(){
		double timeToFirstBorderCollission = Double.POSITIVE_INFINITY;
		double xTime;
		double yTime;

		if(Util.fuzzyLessThanOrEqualTo(getXVelocity(), 0) && !Util.fuzzyEquals(0, getXVelocity())){
			double xDistance = getXCoordinate() - getWorld().getLeftBoundary()-getRadius();
			xTime = xDistance/-getXVelocity();
		}
		else{
			double xDistance =  getWorld().getRightBoundary()-getXCoordinate()-getRadius();
			xTime = xDistance/getXVelocity();
		}
		if(Util.fuzzyLessThanOrEqualTo(getYVelocity(), 0) && !Util.fuzzyEquals(0, getYVelocity())){
			double yDistance = getYCoordinate() - getWorld().getLowerBoundary()-getRadius();
			yTime = yDistance/-getYVelocity();
		}
		else{
			double yDistance =  getWorld().getUpperBoundary()-getYCoordinate()-getRadius();
			yTime = yDistance/getYVelocity();
		}
		if(Util.fuzzyLessThanOrEqualTo(yTime, xTime) && !Util.fuzzyLessThanOrEqualTo(yTime, 0)){
			timeToFirstBorderCollission =yTime;
		}
		else if (!Util.fuzzyLessThanOrEqualTo(xTime, 0)){
			timeToFirstBorderCollission =xTime;
		}
		else
			timeToFirstBorderCollission = Double.POSITIVE_INFINITY;
		if(Util.fuzzyEquals(0, timeToFirstBorderCollission))
			return 0;
		if (Double.isNaN(timeToFirstBorderCollission) )
		{return Double.POSITIVE_INFINITY;
		}
		return timeToFirstBorderCollission;
	}
	/**
	 * 
	 * @param coordinate The coordinate you want to check
	 * @return	...
	 * 			| return(Double.isNan(coordinate));
	 */
	public boolean isValidCoordinate(double coordinate) {
		if(Double.isNaN(coordinate))
			return false;
		return true;
	}

	private Vector getPositionVector(){
		return this.positionVector; //TODO Clonen?
	}

	private Vector positionVector;








	/**
	 * @return The x-velocity of the collisionobject
	 */
	@Basic
	@Raw
	public double getXVelocity(){
		return getVelocityVector().getValueAtPosition(getXPositionInVector());
	}

	/**
	 * 
	 * @param xVelocity The new xVelocity 
	 * @post			If it is a valid velocity, then the given xVelocity is the new xVelocity
	 * 					|if(isValidVelocity(xVelocity, this.getYVelocity())){
						|	(new this).getXVelocity == xVelocity;
	 */
	@Basic 
	@Raw
	private void setXVelocity(double xVelocity){
		//if(isValidVelocity(xVelocity, this.getYVelocity())){
		if(getVelocityVector()!=null)
			this.velocityVector = new Vector(xVelocity, getYVelocity());
		else
			velocityVector = new Vector(xVelocity,0.0);
		//}
	}

	/**
	 * 
	 * @return The y-velocity of the collisionobject
	 */
	@Basic 
	public double getYVelocity(){
		return getVelocityVector().getValueAtPosition(getYPositionInVector());
	}	

	/**
	 * 
	 * @param yVelocity The new yVelocity 
	 * @post			If it is a valid velocity, then the given yVelocity is the new yVelocity
	 * 					|if(isValidVelocity(this.getXVelocity(), yVelocity)){
						|(new this).getYVelocity = yVelocity;
	 */
	@Basic @Raw
	private void setYVelocity(double yVelocity){
		//if(isValidVelocity(this.getXVelocity(), yVelocity)){
		if(getVelocityVector()!=null)
			this.velocityVector = new Vector(getXVelocity(),yVelocity);
		else
			velocityVector = new Vector(0.0, yVelocity);
		//}
	}

	/**
	 * 
	 * @param xVelocity		The current rate at which the x-value changes
	 * @param yVelocity		The current rate at which the y-value changes
	 * @post 	This method sets the new velocity bringing into account that the velocity can't exceed 300000 km/s; if it does, the velocity becomes zero.
	 * 			|if(isValidVellocity(xVelocity,yVelocity) == false){
	 * 			|			(new this).getXVelocity() == 0
	 * 			|			(new this).getYVelocity() == 0}
	 * @post	|If the velocity doesn't exceed 300000 km/s then the given velocity becomes the new velocity.
	 * 			|if(isValidVellocity(xVelocity,yVelocity) == true){
	 * 			|			(new this).getXVelocity() == xVelocity
	 * 			|			(new this).getYVelocity() == yVelocity}
	 */	 	
	protected void setValidVelocity(double xVelocity,double yVelocity){
		if(!isValidVelocity(xVelocity,yVelocity)){
			setXVelocity(0);
			setYVelocity(0);
		}
		else {
			//velocityVector = new Vector(xVelocity,yVelocity);
			setXVelocity(xVelocity);
			setYVelocity(yVelocity);
		}
	}

	/**
	 * @post	...
	 * 			|new.getTotalVelocity(getXVelocity(),getYVelocity()) == getSpeedOfOfLight()
	 */
	@Raw
	public void setVelocityToSpeedOfLight(){
		double vectorAngle = Vector.getResAngleOfPerpendicularVectors(getVelocityVector(), getXPositionInVector(),getYPositionInVector());
		this.setXVelocity(getSpeedOfLight()*Math.cos(vectorAngle));
		this.setYVelocity(getSpeedOfLight()*Math.sin(vectorAngle));
	}

	/**
	 * 
	 * @param xVelocity		The current rate at which the x-value changes
	 * @param yVelocity		The current rate at which the y-value changes
	 * @return	True if the absolute velocity is less then 300000 km/s
	 * 			|double totalVelocity = Math.sqrt(xVelocity^2 + yVelocity^2);
	 * 			|return (totalVelocity <= 3000000 && totalVelocity <= getVelocityConstraint() && !Double.isNan(totalVelocity)) 
	 */
	protected boolean isValidVelocity(double xVelocity, double yVelocity){
		double totalVelocity = getTotalVelocity(xVelocity, yVelocity);
		if((Util.fuzzyLessThanOrEqualTo(totalVelocity, getVelocityConstraint())) && Util.fuzzyLessThanOrEqualTo(totalVelocity, getSpeedOfLight()) && !Double.isNaN(totalVelocity))
			return true;
		return false;
	}

	/**
	 * 
	 * @param xVelocity
	 * @param yVelocity
	 * @return 	...
	 * 			|return (xVelocity^2 + yVelocity^2)^(1/2)
	 */
	public double getTotalVelocity(double xVelocity, double yVelocity){
		return Math.sqrt(Math.pow(xVelocity,2) + Math.pow(yVelocity,2));
	}


	/**
	 * The restriction on the velocity.
	 * @return
	 */
	@Basic
	protected double getVelocityConstraint(){
		return this.velocityConstraint;
	}

	@Immutable
	@Basic
	public double getSpeedOfLight(){
		return this.SPEED_OF_LIGHT;
	}

	//TODO CLONEN? Zo ja -> Wel kijken bij de andere klasses/Methodes op aanpasbaarheid van geheel
	private Vector getVelocityVector(){
		return this.velocityVector;
	}

	private final double SPEED_OF_LIGHT = 300000;
	private final double velocityConstraint = SPEED_OF_LIGHT;
	private Vector velocityVector;






	@Basic
	public double getRadius(){
		return this.radius;
	}

	/**
	 * 
	 * @param radius						The distance between the center of the ship and the outer rim
	 * @throws	IllegalRadiusException		If the given radius is smaller than the radiusconstraint, than the exception is thrown
	 * @return 	true if the value of the radius is a situated between the minimum and maximumradiussize.
	 * 			|if(radius >= radiusConstraint && radius<= maxRadiusConstraint && radius < Double.POSITIVE_INFINITY){result == true}
	 */	
	public boolean isValidRadius(double radius){
		if(Util.fuzzyLessThanOrEqualTo(radius, getMinRadiusConstraint()) || !Util.fuzzyLessThanOrEqualTo(radius, getMaxRadiusConstraint())|| Util.fuzzyEquals(radius, Double.POSITIVE_INFINITY) || Double.isNaN(radius)){
			return false;
		}
		return true;
	}

	/**
	 * Get the minimumRadiusSize
	 * @return
	 */
	@Basic
	private double getMinRadiusConstraint(){
		return this.minRadiusConstraint;
	}
	private double getMaxRadiusConstraint(){
		return this.maxRadiusConstraint;
	}
	//IK ZOU DEZE SETTEN IN DE CONSTRUCTOR EN HIER DE METHODES SETMINRADIUS EN SETMAXRADIUS ABSTRACT MAKEN
	private final double radius;
	private final double minRadiusConstraint = 0; //TODO
	private final double maxRadiusConstraint = Double.POSITIVE_INFINITY;





	@Basic @Raw
	public double getMass() {
		return this.mass;
	}

	/**
	 * 
	 * @param variable The parameter you want to use to calculate/set a new mass. For example: radius, mass,...
	 */
	protected abstract void setMass(double variable);


	/**
	 * 
	 * @param mass
	 * @return 	...
	 * 			| if (Util.fuzzyLessThanOrEqualTo(mass, getMinimumMass()) || !Util.fuzzyLessThanOrEqualTo(mass, getMaximumMass())|| Double.isNaN(mass))
	 * 			| then return false;
	 * 			| else return true;
	 */
	protected boolean isValidMass(double mass){
		if(Util.fuzzyLessThanOrEqualTo(mass, getMinimumMass()) || !Util.fuzzyLessThanOrEqualTo(mass, getMaximumMass())|| Double.isNaN(mass)){
			return false;
		}
		return true;
	}

	protected abstract double getMinimumMass();
	protected abstract double getMaximumMass();
	//protected abstract double getMassToAssign();

	private void setMinimumMass(){
		this.minimumMass = getMinimumMass();
	}

	private void setMaximumMass(){
		this.maximumMass = getMaximumMass();
	}

	// deze velden worden niet gebruikt omdat dit allemaal in de subklassen gebeurd 
	protected double mass;
	private double maximumMass;
	private double minimumMass;






	/**
	 * 
	 * @param duration	The time that a method gets
	 * @return	if the given duration is larger then zero, then result is duration
	 * 			otherwise the duration becomes zero
	 * 			|if(duration>0){result == true}
	 */
	private boolean isValidDuration(double duration){
		if(Util.fuzzyEquals(0.0, duration))
			return true;
		if(	Util.fuzzyLessThanOrEqualTo(duration, 0))
			return false;
		return true;
	}

	/**
	 * 
	 * @param 	collisionObject		The collisionobject to get the distance beween this collisionobject with
	 * @return 	The absolute distance between this collisionobject and the inserted collisionobject 
	 * 			| math.sqrt(Math.pow(collisionObject2.getXCoordinate()-collisionObject1.getXCoordinate())+ Math.pow(collisionObject2.getYCoordinate()-collisionObject1.getYCoordinate()))
	 * @return	The resulting distance is not negative nor Double.NaN
	 * 			|Util.fuzzyLessthanOrEqual(0.0,result) && !Double.isNaN(result)
	 * @throws 	IllegalCollisionObjectException  ...
	 * 											|collisionObject == null
	 * @throws 	IncalculateableRangeException	...
	 * 											|Double.isNan(result)
	 */
	public  double getDistanceBetween(CollisionObject collisionObject) throws IllegalCollisionObjectException, IncalculateableRangeException, IllegalArgumentException {
		if(this==collisionObject)
			return 0;
		if(!isValidCollsionObjectCombination(collisionObject)){
			throw new IllegalCollisionObjectException("Invalid collisionObject");
		}

		double xDiffToTheSecond = Math.pow(this.getXDistanceBetween(collisionObject), 2);
		double yDiffToTheSecond = Math.pow(this.getYDistanceBetween(collisionObject), 2);

		double result = Math.sqrt(xDiffToTheSecond + yDiffToTheSecond);
		if(Double.isNaN(result))
			throw new IncalculateableRangeException("There was a calculation which was not usefull anymore for further calculation");
		return result;
	}

	private  double getXDistanceBetween(CollisionObject collisionObject2){
		return this.getXCoordinate() - collisionObject2.getXCoordinate();
	}

	private  double getYDistanceBetween(CollisionObject collisionObject2){
		return this.getYCoordinate()-collisionObject2.getYCoordinate();
	}


	/**
	 * 
	 * @param collisionObject	The second ship to check whether there is an overlap with the first ship.
	 * @return 	true if this collisionobject is the given collisionobject
	 * 			|if(this==collisionObject)
	 * 			|	then result == true
	 * @return  ...
	 * 			| if (result == true) then
	 * 			| 	Util.fuzzyLessThanOrEqualTo(this.getDistanceBetween(collisionObject), this.getRadius()+collisionObject.getRadius())
	 * 
	 * @throws IllegalCollisionObjectException 				collisionObject == null
	 * @throws IllegalCalculateableRangeException   
	 */
	public  boolean overlap(CollisionObject collisionObject) throws IllegalCollisionObjectException, IncalculateableRangeException{
		if(this == collisionObject){
			return true;
		}
		double totalDistance = getDistanceBetween(collisionObject);
		double totalRadius = this.getRadius()+collisionObject.getRadius();
		if(Util.fuzzyLessThanOrEqualTo(totalDistance,totalRadius)){
			return true;
		}
		return false;
	}


	/**
	 * 
	 * @param collisionObject	The collisionobject to collide with
	 * @return	The resulting distance is not negative nor Double.NaN
	 * 			|Util.fuzzyLessthanOrEqual(0.0,result) && !Double.isNaN(result)
	 * @return 	...
	 * 			|if(result == Double.POSITIVE_INFINITY) then
	 * 			|	for each time in 0.0...Double.POSITIVE_INFINITY
	 * 			| 		if(!Double.isInfinite(time))
	 * 			|			this.move(time);
	 * 			|			collisionObject.move(time);
	 * 			|			!this.overlap(collisionObject);
	 * @return 	...
	 * 			|if(!Double.isInfite(result))
	 * 			|	for each time in 0.0...result
	 * 			|		if(time!=result)
	 * 			|			this.move(time);
	 * 			|			collisionObject.move(time);
	 * 			|			!this.overlap(collisionObject);
	 * 			|		if(Util.fuzzyEquals(time,result)
	 * 			|			this.move(time);
	 * 			|			collisionObject.move(time);
	 * 			|			this.overlap(collisionObject);
	 * 			
	 * @throws IllegalVectorProductException	
	 * @throws IncalculateableRangeException
	 * @throws IllegalRadiusException			...
	 * 											|this.getRadius()+collisionObject.getRadius() == Double.POSITIVE_INFINITY || Double.isNaN(this.getRadius()+collisionObject.getRadius() )
	 * @throws IllegalCollisionObjectException	...
	 * 											|this == collisionObject || this == null
	 */
	public  double getTimeToCollision(CollisionObject collisionObject) throws IllegalVectorProductException,
	IncalculateableRangeException, IllegalRadiusException, IllegalCollisionObjectException{	

		if(!isValidCollsionObjectCombination(collisionObject)){
			throw new IllegalCollisionObjectException("Invalid collisionobject");
		}


		double deltaX = collisionObject.getXCoordinate()-this.getXCoordinate();
		double deltaY = collisionObject.getYCoordinate()-this.getYCoordinate();

		//Alle verschil in snelheden
		double deltaXVelocity = collisionObject.getXVelocity() - this.getXVelocity();
		double deltaYVelocity = collisionObject.getYVelocity() - this.getYVelocity();
		if(isCalculateableDoubleRange(deltaX) && isCalculateableDoubleRange(deltaY) && 
				isCalculateableDoubleRange(deltaXVelocity) && isCalculateableDoubleRange(deltaYVelocity)){
			//VectorCreation
			Vector deltaR = new Vector(deltaX,deltaY);
			Vector deltaV = new Vector(deltaXVelocity,deltaYVelocity);
			double sigmaTotal = this.getRadius() + collisionObject.getRadius();
			if(sigmaTotal == Double.POSITIVE_INFINITY)
				throw new IllegalRadiusException("The sum of the two radia is not applicable for calculation");

			//Results
			double deltaVTimesDeltaR = Vector.calculateDotProduct(deltaV, deltaR);
			double deltaVTimesDeltaV = Vector.calculateDotProduct(deltaV,deltaV);
			double deltaRTimesDeltaR = Vector.calculateDotProduct(deltaR, deltaR);

			//Calculate d from endresult in formula
			double part1 = deltaVTimesDeltaR*deltaVTimesDeltaR;
			double part2 = (sigmaTotal*sigmaTotal - deltaRTimesDeltaR)*deltaVTimesDeltaV;
			double dParam = part1+part2;

			//Resultsplitting
			//if (deltaVTimesDeltaR >= 0){
			if(Util.fuzzyEquals(deltaVTimesDeltaR,0) || !Util.fuzzyLessThanOrEqualTo(deltaVTimesDeltaR, 0)){
				return Double.POSITIVE_INFINITY;
			}
			if (Util.fuzzyLessThanOrEqualTo(dParam, 0)){
				return Double.POSITIVE_INFINITY;
			}
			else{
				return -(deltaVTimesDeltaR+Math.sqrt(dParam))/deltaVTimesDeltaV;			
			}
		}
		else
			throw new IncalculateableRangeException("There was a calculation which isn't applicable for further usage");
	}


	/**
	 * 
	 * @param collisionObject1 The first vessel to compare with collisionObject2 to get the collision position between collisionObject1 and collisionObject2.
	 * @param collisionObject2 The vessel that was compared with collisionObject1 to get the collision position between collisionObject1 and collisionObject2.
	 * @return 	The x- and y-coordinate from the potential collision between collisionObject1 and collisionObject2.
	 *
	 * 			| return {collisionObject1.getXVelocity()*getTimeToCollision(collisionObject1,collisionObject2)+collisionObject1.getXPosition(), collisionObject1.getYVelocity()*getTimeToCollision(collisionObject1,collisionObject2)+collisionObject1.getYPosition(collisionObject1,collisionObject2)}
	 * @throws IllegalVectorProductException
	 * @throws IncalculateableRangeException
	 * @throws IllegalRadiusException
	 * @throws IllegalCollisionObjectException
	 */

	/**
	 * @param collisionObject The collisionobject to collide with
	 * @return	...
	 * 			| if(result == null) then
	 * 			| 	Util.fuzzyEquals(this.getTotalVelocity(),0.0) && Util.fuzzyEquals(collisionObject.getTotalVelocity() ,0.0)
	 * @return	...
	 * 			| if(result != null) then 
	 * 			|	getTimeToCollision() != Double.POSITIVE_INFINITY
	 * 			| 	this.move(getTimeToCollision());
	 * 			|	collisionObject.move(getTimeToCollision());
	 * 			|	double collisionAngle = Math.atan2( this.getYCoordinate()-collisionObject2.getYCoordinate(), this.getXCoordinate()-collisionObject2.getXCoordinate())
	 * 			|	result[0]== this.getXCoordinate + getRadius*Math.cos(collisionAngle);
	 * 			|	result[1]== this.getYCoordinate + getRadius*Math.sin(collisionAngle);
	 * @throws IllegalVectorProductException
	 * @throws IncalculateableRangeException 	...
	 * 											|getTimeToCollision== Double.POSITIVE_INFINITY
	 * @throws IllegalRadiusException
	 * @throws IllegalCollisionObjectException
	 */
	public  double[] getCollisionPosition(CollisionObject collisionObject) throws IllegalVectorProductException,
	IncalculateableRangeException, IllegalRadiusException, IllegalCollisionObjectException{

		double timeLapse = getTimeToCollision(collisionObject);
		if(timeLapse != Double.POSITIVE_INFINITY){
			double collisionAngle = getCollisionAngle( this.getObjectAtColissionPosition(timeLapse),collisionObject.getObjectAtColissionPosition(timeLapse));
			if(!Util.fuzzyEquals(getTotalVelocity(this),0.0))
				return this.getAlternCollisionPosition(timeLapse, collisionAngle);
			if(!Util.fuzzyEquals(getTotalVelocity(collisionObject),0.0))
				return collisionObject.getAlternCollisionPosition(timeLapse, collisionAngle);
			else
				return null;
		}
		else
			throw new IncalculateableRangeException("The ships will not collide");

	}


	private  double getCollisionAngle(CollisionObject collisionObject1,CollisionObject collisionObject2){
		double x = collisionObject1.getXDistanceBetween(collisionObject2);
		double y = collisionObject1.getYDistanceBetween(collisionObject2);
		return Math.atan2(y, x);
	}

	private  double[] getAlternCollisionPosition(double timeLapse, double collisionAngle){
		double xCollisionPosition,yCollisionPosition,xRadiusExtra,yRadiusExtra;

		xRadiusExtra = this.getRadius()*Math.cos(collisionAngle);
		yRadiusExtra = this.getRadius()*Math.sin(collisionAngle);
		CollisionObject cloneObj = this.clone();
		cloneObj.move(timeLapse);
		xCollisionPosition = this.getXCoordinate() + xRadiusExtra;
		yCollisionPosition = this.getYCoordinate() + yRadiusExtra;

		double[] collisionLocation = {xCollisionPosition,yCollisionPosition};
		return collisionLocation;
	}

	private  boolean isValidCollsionObjectCombination(CollisionObject collisionObject){
		return (collisionObject!=null && this != collisionObject);
	}

	private  double getTotalVelocity(CollisionObject collisionObject){
		return Math.sqrt(Math.pow(collisionObject.getXVelocity(),2)+Math.pow(collisionObject.getYVelocity(), 2));
	}

	private  boolean isCalculateableDoubleRange(double value) throws IncalculateableRangeException{
		if(value == Double.POSITIVE_INFINITY || value == Double.NEGATIVE_INFINITY || Double.isNaN(value)){
			throw new IncalculateableRangeException("The value isn't applicable for further usage");
		}
		return true;
	}

	//protected abstract CollisionObject getObjectAtColissionPosition(double timeLapse);
	protected abstract CollisionObject clone();

	private CollisionObject getObjectAtColissionPosition(double timeLapse){
		CollisionObject cloneObject = this.clone();
		cloneObject.move(timeLapse);
		return cloneObject;
	}






	@Basic
	public boolean isShip(){
		if(Ship.class.isInstance(this))
			return true;
		return false;
	}

	/**
	 * Check whether this collisionobject is an asteroid.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	@Basic
	public boolean isAsteroid(){
		if(Asteroid.class.isInstance(this))
			return true;
		return false;
	}

	/**
	 * Check whether <code>o</code> is a bullet.
	 * 
	 * You can use the <code>instanceof</code> operator to implement this method.
	 */
	@Basic
	public boolean isBullet(){
		if(Bullet.class.isInstance(this))
			return true;
		return false;
	}



	public abstract void collisionReactionWith(CollisionObject collisionObject);


	/**
	 * 
	 * @param collisionObject
	 * @throws NullPointerException
	 */
	private void setVelocityAfterObjectCollision(CollisionObject collisionObject) throws NullPointerException, IncalculateableRangeException{
		//TODO
		double oldXVelocityObject1,oldXVelocityObject2,oldYVelocityObject1,oldYVelocityObject2, jParam, jX, jY, newXVelocityObject1, newYVelocityObject1, newXVelocityObject2, newYVelocityObject2;


		//INITIALISATION TEMP VAR
		//THIS OBJECT = OBJECT1: SPECIFICATIONS@STATE
		oldXVelocityObject1 	= this.getXVelocity();
		oldYVelocityObject1 	= this.getYVelocity();
		double massObject1 		= this.getMass();
		double radiusObject1 	= this.getRadius();

		//OBJECT 2 = COLLISIONOBJECT: SPECIFICATIONS@STATE
		oldXVelocityObject2 	= collisionObject.getXVelocity();
		oldYVelocityObject2 	= collisionObject.getYVelocity();
		double massObject2 		= collisionObject.getMass();
		double radiusObject2 	= collisionObject.getRadius();

		//RESULTING FROM BOTH OBJECTS
		double totalRadius 		= radiusObject1+radiusObject2;
		double totalMass 		= massObject1 + massObject2;
		// das weer een berekening dus ik denk da  der nei veel aan te doen is
		//TODO codeduplicatie van getTimeToCollision ~ echt abstraheren? Ik denk het niet want dit zouden zogezegd twee totaal verschillende methodes kunnen zijn... evt deltaV etc maar echt nuttig is het niet
		double deltaX 			= collisionObject.getXCoordinate()-this.getXCoordinate();
		double deltaY			= collisionObject.getYCoordinate()-this.getYCoordinate();
		double deltaXVelocity 	= collisionObject.getXVelocity() - this.getXVelocity();
		double deltaYVelocity 	= collisionObject.getYVelocity() - this.getYVelocity();
		double deltaVTimesDeltaR;


		if(	//CHECK WHETHER NO ISNAN OR INFINITY
				isCalculateableDoubleRange(deltaX) 			&& isCalculateableDoubleRange(deltaY) 
				&& 	isCalculateableDoubleRange(deltaXVelocity) 	&& isCalculateableDoubleRange(deltaYVelocity)
				&& 	isCalculateableDoubleRange(totalMass) 		&& isCalculateableDoubleRange(totalRadius)
				){
			//"VECTORCREATION" NOT A REAL VECTOR FROM DEFINED CLASS SINCE JUST NEED FOR TEMP USAGE. IF CALCULATEDOTPRODUCT IS MOVED TO THE CLASS VECTOR THESE SHOULD BE VECTORS
			Vector deltaR = new Vector(deltaX,deltaY);
			Vector deltaV = new Vector(deltaXVelocity,deltaYVelocity);
			//RESULT FROM DOTPRODUCT
			deltaVTimesDeltaR = Vector.calculateDotProduct(deltaV, deltaR);
		}
		else{//THROWN IF INCALCULATABLE
			throw new IncalculateableRangeException("There was a calculation which isn't applicable for further usage");
		}

		//FURTHER CALCULATIONS FOR RESULT
		jParam 	= 2*this.getMass()*collisionObject.getMass()*deltaVTimesDeltaR/totalRadius/totalMass;
		jX		= jParam*deltaX / totalRadius;
		jY		= jParam*deltaY / totalRadius;

		//RESULTING VELOCITIES
		newXVelocityObject1 =	jX/massObject1 	+	oldXVelocityObject1;
		newYVelocityObject1 =	jY/massObject1 	+	oldYVelocityObject1;
		newXVelocityObject2 =	-jX/massObject2 + 	oldXVelocityObject2;
		newYVelocityObject2	=	-jY/massObject2 +	oldYVelocityObject2;

		//SETTING THE VELOCITY
		//TODO Abstraheren naar kleinere methodes, code duplicatie

		//OBJECT1
		if(isValidVelocity(newXVelocityObject1, newYVelocityObject1)){
			this.setValidVelocity(newXVelocityObject1, newYVelocityObject1);

			//TODO NOG BEPALEN BIJ WELKE DE SNELHEID HOORT! BOVEN/ONDER/LINKS/RECHTS!!!!!!!!!!!!!!!!!!!!!!!
		}
		else{
			this.setVelocityToSpeedOfLight();
		}

		//OBJECT2
		if(isValidVelocity(newXVelocityObject2, newYVelocityObject2)){
			collisionObject.setValidVelocity(newXVelocityObject2, newYVelocityObject2);
		}
		else{
			//			double vectorAngle2 = getResAngleOfPerpendicularVectors(newXVelocityObject2, newYVelocityObject2);
			//			this.setXVelocity(getSpeedOfLight()*Math.cos(vectorAngle2));
			//			this.setYVelocity(getSpeedOfLight()*Math.sin(vectorAngle2));
			collisionObject.setVelocityToSpeedOfLight();
		}
	}

	/**
	 * Sets the velocity after collision with the boundarys of the world this collisionobject is in. 
	 * If it collides with a boundary, the velocity of the object is turned to the opposite direction.
	 */
	private void setVelocityAfterBoundaryCollision(){
		if(isInWorld()){
			if(isLeftRightCollision()){
				double newXVelocity = -getXVelocity();
				setValidVelocity(newXVelocity, getYVelocity() );
			}
			if(isUpperLowerCollision()){
				double newYVelocity = -getYVelocity();
				setValidVelocity(getXVelocity(), newYVelocity);
			}
		}
		else
			throw new IllegalStateException();
	}

	/**
	 * 
	 * @return 	true if this collisionobjct touches to border or exceeds it.
	 * 			|result ==  Util.fuzzyLessThanOrEqualTo(this.getXCoordinate() - this.getRadius(), this.getWorld().getLeftBoundary())||
	 *						Util.fuzzyLessThanOrEqualTo(this.getYCoordinate() - this.getRadius(),this.getWorld().getLowerBoundary())||
	 *						Util.fuzzyLessThanOrEqualTo(this.getWorld().getRightBoundary(),this.getXCoordinate() + this.getRadius())||
	 *						Util.fuzzyLessThanOrEqualTo(this.getWorld().getUpperBoundary(),this.getYCoordinate() + this.getRadius()))
	 * @throws	IllegalStateException !isInWorld()
	 */
	public boolean exceedsWorldBoundaries() throws IllegalStateException{
		if(isInWorld()){
			return(isLeftRightCollision() || isUpperLowerCollision());
		}
		else
			throw new IllegalStateException();
	}

	/**
	 * Whether or not the object collide with the left or the right boundary (NOT TO CHECK WHICH SIDE OF THOSE TWO!!)
	 */
	private boolean isLeftRightCollision(){
		double right =this.getXCoordinate() + this.getRadius();		double rightBoundary = this.getWorld().getRightBoundary();
		double left = this.getXCoordinate() - this.getRadius();		double leftBoundary = this.getWorld().getLeftBoundary();
		if(Util.fuzzyLessThanOrEqualTo(left,leftBoundary) 	||Util.fuzzyLessThanOrEqualTo(rightBoundary,right))
			return true;
		return false;
	}

	/**
	 * Whether or not the object collide with the upper or lower boundary (NOT TO CHECK WHICH SIDE OF THOSE TWO!!)
	 * @return
	 */
	private boolean isUpperLowerCollision(){
		double upper = this.getYCoordinate() + this.getRadius();	double upperBoundary = this.getWorld().getUpperBoundary();
		double lower = this.getYCoordinate() - this.getRadius();	double lowerBoundary =this.getWorld().getLowerBoundary() ;		
		if(	Util.fuzzyLessThanOrEqualTo(lower,lowerBoundary)||Util.fuzzyLessThanOrEqualTo(upperBoundary,upper))
			return true;
		return false;
	}










	private static enum State {
		INWORLD, NOWORLD, DESTROYED;
	}

	private State state;

	@Raw @Basic
	private State getState() {
		State state =  this.state;
		return state;
	}
	/**
	 * 
	 * @param state
	 * @post	||if(state != null){
	 * 			||	((new) this).state == state);}
	 */
	private void setState(State state) {
		assert (state != null);
		this.state = state;
	}

	/**
	 * @post	||this.isInWorld() == true
	 */
	private void setStateWorld(){
		setState(State.INWORLD); 
	}

	/**
	 * @post	||this.isInNoWorld() == true
	 */
	private void setStateNoWorld(){
		setState(State.NOWORLD);
	}

	/**
	 * @post	||this.isDestroyed == true
	 */
	private void setStateDestroyed() {
		setState(State.DESTROYED);
	}

	private boolean hasProperState(){
		return isDestroyed() ^ isInNoWorld() ^ isInWorld();
	}

	@Basic
	public boolean isDestroyed(){
		return this.getState() == State.DESTROYED;
	}

	@Basic
	public boolean isInNoWorld(){
		return this.getState() == State.NOWORLD;
	}

	@Basic
	public boolean isInWorld(){
		return this.getState() == State.INWORLD;
	}

	/**
	 * @post 	...
	 * 			|!new.getWorld().hasAsCollisionObject(this)
	 * @post	...
	 * 			|new.isDestroyed()
	 * @post	...
	 * 			|new.getXPosition() 	== null
	 * 			|&&new.getYPosition() 	== null
	 * @post	...
	 * 			|new.getXVelocity() 	== null
	 * 			|&& new.getYVelocity() 	== null
	 * @post	...
	 * 			|this.getWorld() 		== null
	 */
	@Raw
	public void destroy(){
		if(isInWorld()){//!isDestroyed()){
			if(this.getWorld() != null){
				World oldWorld = this.getWorld();
				setWorld(null);
				oldWorld.removeAsCollisionObject(this);
			}
			setStateDestroyed();
			this.positionVector = null;
			this.velocityVector = null;
		}
	}






	/**
	 * 
	 * @param world
	 * @return	...
	 * 			| if(result == false) then
	 * 			|	this.isDestroyed() && world != null 
	 * @return	...
	 * 			| if(result == false) then
	 * 			|	!this.isDestroyed() && world == null && !world.canHaveAsCollisionObject(this)
	 */
	@Raw
	public boolean canHaveAsWorld(World world){
		if(this.isDestroyed())
			return world == null;
		return (world!=null && world.canHaveAsCollisionObject(this));
	}

	/**
	 * @param world
	 * @post	...
	 * 			|if(world == null) then
	 * 			| 	new.isInNoWorld()
	 * @post	...
	 * 			|if(world!=null) then
	 * 			|	new.isInWorld() && new.getWorld == world && word.hasAsCollisionObject((new this)) == true)
	 * @throws IllegalStateException ...|
	 * 								this.isDestroyed()
	 */
	@Raw
	public void setWorld(World world) throws IllegalStateException{
		if(isInWorld()){
			if(world == null);
			this.world = world;
			this.setStateNoWorld();
		}
		else if(isInNoWorld()){
			if(world!=null){
				this.world = world;
				world.addAsCollisionObject(this);
				this.setStateWorld();
			}
		}
		else if(isDestroyed())
			throw new IllegalStateException();
		else{
			throw new IllegalStateException();
		}
	}

	@Basic @Raw
	public World getWorld(){
		return this.world;
	}

	private boolean hasValidWorld(){
		if(isDestroyed())
			return getWorld() == null;
		if(isInNoWorld())
			return getWorld() == null;
		if(isInWorld())
			return (world!=null && this.getWorld().canHaveAsCollisionObject(this) && this.canHaveAsWorld(world) && hasProperState());
		return false;
	}

	private World world;

	public boolean isValidObject(){
		boolean one = isValidCoordinate(this.getYCoordinate());
		boolean two = isValidCoordinate(this.getXCoordinate());
		boolean three = isValidMass(this.getMass());
		boolean four = isValidRadius(this.getRadius());
		boolean five = isValidVelocity(this.getXVelocity(), this.getYVelocity());
		boolean six = hasValidWorld();
		boolean seven = hasProperState();
		boolean valid = (isValidCoordinate(this.getYCoordinate()) && isValidCoordinate(this.getXCoordinate()) && isValidMass(this.getMass()) && isValidRadius(this.getRadius()) && isValidVelocity(this.getXVelocity(), this.getYVelocity()) && hasValidWorld() && hasProperState());
		return valid;
	}

	/**
	 * @param collisionObject
	 * @effect	...
	 * 			|setVelocityAfterObjectCollision(collisionObject)
	 */
	public void bounceOffObject(CollisionObject collisionObject){
		setVelocityAfterObjectCollision(collisionObject);
	}



	public void bounceOffBoundary(){ //TODO
		setVelocityAfterBoundaryCollision();
		setPositionAfterBoundaryCollision();
		if(this.isBullet()){
			((Bullet)this).incrementNbOfBounces();
			if(((Bullet)this).getNbOfBounces() == 1+((Bullet)this).getMaxNbOfBounces()){
				((Bullet)this).destroy();

			}
		}

	}

	private void setPositionAfterBoundaryCollision(){
		if(isLeftRightCollision()){
			if(Util.fuzzyLessThanOrEqualTo(this.getXCoordinate()-getRadius(),this.getWorld().getLeftBoundary())) {
				setXCoordinate(this.getWorld().getLeftBoundary()+getRadius());
			}
			else{
				setXCoordinate(this.getWorld().getRightBoundary()-getRadius());
			}
		}
		if(isUpperLowerCollision()){
			if(Util.fuzzyLessThanOrEqualTo(this.getYCoordinate()-getRadius(), getWorld().getLowerBoundary())){
				setYCoordinate(getWorld().getLowerBoundary()+getRadius());
			}
			else{
				setYCoordinate(getWorld().getUpperBoundary()-getRadius());
			}
		}
	}


	@Override
	public String toString(){
		String line="";
		if(isDestroyed())
			line = " And is destroyed.";
		if(isInNoWorld())
			line = " And is not located in any world.";
		if(isInWorld())
			line = " And is located in: " + getWorld().toString();

		return 
				" A position of: " 	+ 	"\n\t x: "	+getXCoordinate()	+	"\n\t y: " 	+ 	getYCoordinate()	+ 
				"\n A speed of: " 	+ 	"\n\t x: " 	+ getXVelocity()	+	"\n\t y: " 	+ 	getYVelocity()		+
				"\n And " 			+	"a radius with size: \n\t" 		+	getRadius() +	"\n"+
				"\n With the mass of: \n\t" +	getMass()				+
				"\n"+ line;
	}
}